package com.netty.clientserver.util;

import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.netty.clientserver.command.Command;
import com.netty.clientserver.packet.response.ResponseVo;
import com.netty.common.entity.User;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.connection.ReactiveStreamCommands;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author LiuXiaoYu
 * @date 2022/2/3- 18:28
 */
@Slf4j
public class ConnectUtils {

    /**3个Map均未考虑内存溢出*/

    /**Map 绑定userId和Channel*/
    private static final Map<String, Channel> USER_CHANNEL_MAP=new ConcurrentHashMap<>();
    /**私聊和群聊的所有channel*/
    private static final ChannelGroup CHANNEL_GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    /**这是群聊的channel集合,但是群聊有id,无法确定是哪个群聊的groupChannel*/
    private static final Map<String,List<Channel>> GROUP_CHANNEL=new ConcurrentHashMap<>();
    /**私聊一对一绑定*/
    private static final Map<String,String> SINGLE_BIND_MAP=new ConcurrentHashMap<>();

    /**
     * 用户和channel进行绑定
     * 并将用户保存在channel的属性中以此来判断是否
     * 已经登录了
     * @param user
     * @param channel
     */
    public static void bindChannel(User user, Channel channel) {

            //如果之前已经绑定过了则移除旧的绑定新的
            if (USER_CHANNEL_MAP.containsKey(user.getUserId().toString())){
                Channel oldChannel = USER_CHANNEL_MAP.get(user.getUserId().toString());
                USER_CHANNEL_MAP.remove(user.getUserId().toString());
                //关闭旧的
                oldChannel.close();
            }
            // 双向绑定
            // channel -> userId
            AttributeKey<String> key = AttributeKey.valueOf("userId");
            channel.attr(key).set(user.getUserId().toString());

            // userId -> channel
            USER_CHANNEL_MAP.put(user.getUserId().toString(), channel);

            //channel->groupchannel是否是群聊channel
           //  GROUP_CHANNEL.put(channel,groupChannel);
    }

    /**
     * 从通道中获取userId。只要userId和channel绑定，这个方法就一定能获取的到
     * @param channel
     * @return
     */
    public static String getUserId(Channel channel) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        return channel.attr(key).get();
    }

    public static void add(Channel channel) {
        CHANNEL_GROUP.add(channel);
    }

    public static void remove(Channel channel) {

            String userId = getUserId(channel);

            // userId有可能为空。可能chanelActive之后，
            // 由于前端原因（或者网络原因）没有及时绑定userId。
            // 此时netty认为channelInactive了，就移除通道，这时userId就是null
            if (!StringUtils.isEmpty(userId)) {
                USER_CHANNEL_MAP.remove(userId); 
            }

            //移除channel
            CHANNEL_GROUP.remove(channel);

            //移除群聊channel
            if (GROUP_CHANNEL.size()>0){
                //判断GROUP_Channel这个map集合的value当中是否包含了这个channel
                GROUP_CHANNEL.forEach((groupId,groupChannels)->{
                    //如果包含了这个channel就移除
                    groupChannels.remove(channel);
                    if (groupChannels.size()==0){
                        GROUP_CHANNEL.remove(groupId);
                    }
                });
            }
            //移除点对点私聊
             SINGLE_BIND_MAP.remove(userId);
            // 关闭channel
            channel.close();

    }

    /**
     * 从群channel和用户绑定channle中移除Channel
     * 最后关闭
     * @param userId
     */
    public static void remove(String userId) {
            Channel channel = USER_CHANNEL_MAP.get(userId);
            USER_CHANNEL_MAP.remove(userId);
            CHANNEL_GROUP.remove(channel);
         //   GROUP_CHANNEL.remove(channel);
            // 关闭channel
            if (!ObjectUtils.isEmpty(channel)) {
                channel.close();
            }

    }

    /**
     * 判断用户是否在线
     * map和channelGroup中均能找得到对应的channel说明用户在线
     * @return      在线就返回对应的channel，不在线返回null
     */
    public static Channel isBind(String userId) {
        Channel channel = USER_CHANNEL_MAP.get(userId);
        if (ObjectUtils.isEmpty(channel)) {
            return null;
        }
        return CHANNEL_GROUP.find(channel.id());
    }
    /**
     * 判断用户是否在线
     * map和channelGroup中均能找得到对应的channel说明用户在线
     * @return      在线就返回对应的channel，不在线返回null
     */
    public static Boolean isBindChannel(String userId) {
        Channel channel = USER_CHANNEL_MAP.get(userId);
        if (ObjectUtils.isEmpty(channel)) {
            return false;
        }
        return true;
    }
    /**
     * 判断Channel是否还存在
     * @param channel
     * @return
     */
    public static boolean isBind(Channel channel) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        String userId = channel.attr(key).get();
        return !ObjectUtils.isEmpty(userId) &&
                !ObjectUtils.isEmpty(USER_CHANNEL_MAP.get(userId));
              //  &&GROUP_CHANNEL.containsKey(channel);
    }

    /**
     * 如果是群组id，则获取该群所有的群聊channel
     * @param groupId
     * @return
     */
    public static List<Channel> getListGroupChannel(String groupId){
        List<Channel> channelList = GROUP_CHANNEL.get(groupId);
        if (channelList==null){
            return new ArrayList<>();
        }
        return channelList;
    }

    /**
     *
     * @param groupId
     * @param channel
     */
    public static void putGroupChannel(String groupId,Channel channel){
        //获取Map
        List<Channel> listGroupChannel = getListGroupChannel(groupId);
        if (listGroupChannel.size() > 0){
            //说明里面有 直接add进去
            listGroupChannel.add(channel);
            //然后放进map 如果key相同直接覆盖
            GROUP_CHANNEL.put(groupId,listGroupChannel);
        }else {
            //说明没有 那就new一个List放进去
            List<Channel> channelList = new ArrayList<>();
            channelList.add(channel);
            GROUP_CHANNEL.put(groupId, channelList);
        }
    }
   /* public static void forceOffLine(String userId) {
        Channel channel = isBind(userId);
        if (!ObjectUtils.isEmpty(channel)) {
            // 推送下线通知
            MsgModel<Object> msgModel = new MsgModel<>();
            msgModel.setAction(MsgActionEnum.FORCE_OFFLINE.type);
            msgModel.setData(MsgActionEnum.FORCE_OFFLINE.content);
            pushMsg(userId, msgModel);

            // 移除通道。服务端单方面关闭连接。前端心跳会发送失败
            remove(userId);
        }
    }*/

//    /**
//     * 消息推送
//     * @param receiverId
//     * @param messageVo
//     */
//    public static void pushMsg(String receiverId, MessageVo messageVo) {
//        Channel receiverChannel = isBind(receiverId);
//        if (!ObjectUtils.isEmpty(receiverChannel)) {
//            TextWebSocketFrame frame = new TextWebSocketFrame(toJson(messageVo));
//            receiverChannel.writeAndFlush(frame);
//        } else {
//            // 离线状态
//            log.info("{} 用户离线", receiverId);
//        }
//    }

//    private static String toJson(MessageVo messageVo) {
//        // 在线，就推送；离线，不做处理
//        ObjectMapper mapper = SpringUtils.getBean(ObjectMapper.class);
//        try {
//            return mapper.writeValueAsString(messageVo);
//        } catch (JsonProcessingException e) {
//            e.printStackTrace();
//            return null;
//        }
//    }

    /**
     * 私聊或者群聊时将自己的发送的响应回去
     */
    public static ByteBuf getByteBuf(ChannelHandlerContext ctx,String message,String ownerImage) {
        ByteBuf byteBuf = ctx.channel().alloc().buffer();
        ResponseVo responseVo = new ResponseVo()
                .setOwnerImage(ownerImage)
                .setType(Command.SELF_RESPONSE)
                .setDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .setMessage(message).setStatus(HttpStatus.HTTP_OK);
        byte []bytes =  JSONObject.toJSONString(responseVo).getBytes(StandardCharsets.UTF_8);
        byteBuf.writeBytes(bytes);
        return byteBuf;
    }

    public synchronized static void print() {
        log.info("所有通道的长id：");
        for (Channel channel : CHANNEL_GROUP) {
            log.info(channel.id().asLongText());
        }

        log.info("userId -> channel 的映射长度:"+USER_CHANNEL_MAP.size());
        for (Map.Entry<String, Channel> entry : USER_CHANNEL_MAP.entrySet()) {
            log.info("userId: {} ---> channelId: {}", entry.getKey(), entry.getValue().id().asLongText());
        }
        log.info("群组Map长度:"+ GROUP_CHANNEL.size());
        for (Map.Entry<String,List<Channel>> entry : GROUP_CHANNEL.entrySet()) {
            log.info("群组id:{},该群在线的群组channel:{}", entry.getKey(), entry.getValue());
            List<Channel> channelList = entry.getValue();
            for (Channel channel:channelList){
                log.info("群组里面的channel->{}",channel.id().asLongText());
            }
        }
        log.info("点对点私聊Map长度:"+SINGLE_BIND_MAP.size());
        for (Map.Entry<String,String> entry : SINGLE_BIND_MAP.entrySet()){
            log.info("点对点私聊:{}->{}",entry.getKey(),entry.getValue());
        }
    }


    /**
     * 判断当前channel 是不是这个groupId下的群聊channel
     * @param groupId
     * @param memberChannel
     * @return
     */
    public static boolean isThisGroupChannel(String groupId, Channel memberChannel) {
        List<Channel> channelList = GROUP_CHANNEL.get(groupId);
        if (channelList!=null&&channelList.size()>0){
            return channelList.contains(memberChannel);
        }
        return false;
    }

    /**
     * 判断一个channel是不是群聊channel
     * 不是群聊channel返回true
     * 是则返回false
     */
    public static boolean whetherGroupChannel(Channel channel){
       Collection<List<Channel>> allGroupChannels = GROUP_CHANNEL.values();
        for (List<Channel> channelList:allGroupChannels){
            if (channelList.contains(channel)){
                return false;
            }
        }
        return true;
    }

    /**
     * 判断是不是点对点私聊
     */
    public static boolean peerToPeerPrivateChat(Channel userChannel){
        //拿到userId
        String userId=getUserId(userChannel);
        String toUserIdInMap = SINGLE_BIND_MAP.get(userId);
        //反向获取
        String reverse = SINGLE_BIND_MAP.get(toUserIdInMap);
        if (reverse!=null){
            return userId.equals(reverse);
        }
        return false;
    }


    /**
     * 绑定点对点私聊
     * @param userId
     * @param toUserId
     */
    public static void putPeerToPeerSingleChat(String userId, String toUserId) {
        SINGLE_BIND_MAP.put(userId,toUserId);
    }
}
